package com.feib.soeasy.security;

import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;

import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.util.RequestMatcher;

import com.feib.soeasy.dao.UserLoginControlDao;
import com.feib.soeasy.model.User;
import com.feib.soeasy.model.UserLoginControl;
import com.feib.soeasy.util.DateUtil;
import com.feib.soeasy.util.SpringContextUtil;

/**
 * @title (#)SoeasyFilterSecurityInterceptor.java<br>
 * @description <br>
 * @author Anson Tsai<br>
 * @version 1.0.0 2010/11/25
 * @copyright Far Eastern International Bank Copyright (c) 2010<br>
 * @2010/11/30 create by Anson Tsai<br>
 */
public class SoeasyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(SoeasyFilterSecurityInterceptor.class);

    private static final String SECURITY_AUTHENTICATION_PRINCIPAL_ANONYMOUS_USER = "anonymousUser";

    private static final Integer DEFAULT_SECURITY_SESSION_TIMEOUT_NUM = new Integer(20);
    
    private Integer sessionTimeOut;

    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
    /* eBIlling功能提升_第二階段, eBilling分由不同頁面登入 */
    @Resource(name="soeasyAuthenticationEntryPoint")
    private SoeasyDelegatingAuthenticationEntryPoint soeasyAuthenticationEntryPoint;

    /*
     * (non-Javdoc)
     * 
     * @see javax.servlet.Filter#destroy()
     */
    @Override
    public void destroy() {

    }

    /*
     * (non-Javdoc)
     * 
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
     * javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
                    ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
                
        if (logger.isTraceEnabled()) {
            logger.trace("doFilter(ServletRequest, ServletResponse, FilterChain) - start"); //$NON-NLS-1$
            logger.trace("httpRequest.getRequestURI() : " + httpRequest.getRequestURI());
        }
        
        if (null != SecurityContextHolder.getContext().getAuthentication() &&
                        !SECURITY_AUTHENTICATION_PRINCIPAL_ANONYMOUS_USER.equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal())) {
            
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String groupNo = null;// 帳單業者統一編號
            String userId = null;// 使用者名稱
            UserLoginControl c = null;
            UserLoginControlDao dao = (UserLoginControlDao) SpringContextUtil.getBean("userLoginControlDao");
            
            if (null != authentication.getPrincipal() && authentication.getPrincipal() instanceof SoeasyUserDetails) {
                logger.trace("從 Spring Security principal 取得 User 物件，依據User物件取得 UserLoginControl 物件");
                User user = ((SoeasyUserDetails) authentication.getPrincipal()).getUser();
                
                groupNo = user.getGroup().getGroupNo();
                userId = user.getUserId();
                c = dao.findByUer(user);                
            }
            else
            {
                String name = authentication.getName();
                String[] userNameTemp = name.split("_conjunction_");
                groupNo = userNameTemp[0];
                userId = userNameTemp[1];

                c = dao.findByUerIdGroupNo(userId, groupNo);
            }
                        
            /* eBIlling功能提升_第二階段, eBilling分由不同頁面登入 */
            String targetUrl = null;
            if (null != groupNo)
            {
            	if ("000000000001".equals(groupNo))
            		targetUrl = "/admlogin.jsp";
            	else
            		targetUrl = "/login.jsp";
            }
            else
            {
            	LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = soeasyAuthenticationEntryPoint.getEntryPoints();
                targetUrl = ((LoginUrlAuthenticationEntryPoint)soeasyAuthenticationEntryPoint.getDefaultEntryPoint()).getLoginFormUrl();
                for (RequestMatcher requestMatcher : entryPoints.keySet()) {
        			if (requestMatcher.matches(httpRequest))
        			{
        				LoginUrlAuthenticationEntryPoint ep = (LoginUrlAuthenticationEntryPoint)entryPoints.get(requestMatcher);
        				targetUrl = ep.getLoginFormUrl();
        			}
        		}
            }
            
            
            if (this.isDuplicatedLogin(httpRequest, c))
            {
                httpResponse.sendRedirect(httpRequest.getContextPath() + targetUrl +"?error=7");
                return;
            }
                        
            if (null != c) 
            {
                // 強迫登出
                if (!   (null == c.getActived() ? false : c.getActived().booleanValue()))
                {
                    SecurityContextHolder.clearContext();
                    if (null != httpRequest.getSession(false))
                    	httpRequest.getSession(false).invalidate();
                    httpResponse.sendRedirect(httpRequest.getContextPath() + targetUrl + "?error=9");
                    return;
                }
                
                if (this.isSessionTimeout(c)) 
                {
                    c.setActived(Boolean.FALSE);
                    c.setActivityTime(new Date());
                    dao.save(c);
                    
                    SecurityContextHolder.clearContext();
                    if (null != httpRequest.getSession(false))
                    	httpRequest.getSession(false).invalidate();
                    httpResponse.sendRedirect(httpRequest.getContextPath() + targetUrl + "/?error=8");
                    return;
                }
                else 
                {                  
                    if (-1 != httpRequest.getRequestURI().indexOf(".action"))
                    {
                        c.setActivityTime(new Date());
                        dao.save(c);
                    }
                }
            }
        }

        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);

        if (logger.isTraceEnabled()) {
            logger.trace("doFilter(ServletRequest, ServletResponse, FilterChain) - end"); //$NON-NLS-1$  
        }
    }

    /**
     * 檢查 User 上一次的 activity time 是否已超過指定的時間, 如果是則將之 Kick Out
     * 
     * @param userLoginControl
     */
    private boolean isSessionTimeout(UserLoginControl c) {
        
        if (null == sessionTimeOut)
            sessionTimeOut = DEFAULT_SECURITY_SESSION_TIMEOUT_NUM;
        
        if (null != c && sessionTimeOut.intValue() > 0) {
            Date activityTime = c.getActivityTime();

            if (null != activityTime) {
                if (!DateUtil.isWithinMinutes(activityTime, sessionTimeOut.intValue())) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Session TimeOut , mins : " + sessionTimeOut.intValue() + " , Last Activity Time : " + activityTime);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 檢查目前 user login control table 中記錄的 sessionId 是否和目前 session 相同 ，且URL是否為Login.action或login.jsp
     * 
     * @param request
     * @param c
     * @return
     */
    private boolean isDuplicatedLogin(HttpServletRequest r, UserLoginControl c) {
        
        if (null == c)
            return false;
        
        if (null != SecurityContextHolder.getContext().getAuthentication().getDetails()) {
            String sessionId = ((WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getSessionId();

            if (logger.isTraceEnabled()) {
                logger.trace("Current Session Id : " + sessionId + ", UserLoginControl Session Id : " + c.getSessionId());
            }

            if (sessionId.equals(c.getSessionId()) && 
                            (r.getRequestURI().endsWith("Login.action") || r.getRequestURI().endsWith("login.jsp")) 
                            && StringUtils.isBlank(r.getParameter("error"))) 
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 
     * @param fi
     * @throws IOException
     * @throws ServletException
     */
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }
        finally {
            super.afterInvocation(token, null);
        }
    }

    /*
     * (non-Javdoc)
     * 
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }

    /*
     * (non-Javdoc)
     * 
     * @see
     * org.springframework.security.access.intercept.AbstractSecurityInterceptor
     * #getSecureObjectClass()
     */
    @Override
    public Class<? extends Object> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    /*
     * (non-Javdoc)
     * 
     * @see
     * org.springframework.security.access.intercept.AbstractSecurityInterceptor
     * #obtainSecurityMetadataSource()
     */
    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return securityMetadataSource;
    }

    /**
     * @return 傳回 securityMetadataSource。
     */
    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return securityMetadataSource;
    }

    /**
     * @param securityMetadataSource 要設定的 securityMetadataSource。
     */
    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
        this.securityMetadataSource = securityMetadataSource;
    }

    
    /**
     * @return the sessionTimeOut
     */
    public Integer getSessionTimeOut() {
        return sessionTimeOut;
    }

    
    /**
     * @param sessionTimeOut the sessionTimeOut to set
     */
    public void setSessionTimeOut(Integer sessionTimeOut) {
        this.sessionTimeOut = sessionTimeOut;
    }

}
